using System;
using System.Collections.Generic;
using System.Net;
using System.Text;

using Kannon.Core;

namespace Kannon.Server
{
    public class HttpListenerWorker
    {
        private static readonly IDictionary<string, Action<HttpListenerResponse, string>>
            RestrictedHeaderActions = new Dictionary<string, Action<HttpListenerResponse, string>> {
                { "Content-Length", (response, s) => { if(!string.IsNullOrEmpty(s)) response.ContentLength64 = long.Parse(s); } },
            };

        private readonly Type applicationType;

        public HttpListenerWorker(Type applicationType)
        {
            this.applicationType = applicationType;
        }

        public void HandleRequest(object context)
        {
            HandleRequestInternal((HttpListenerContext)context);
        }

        public void HandleRequestInternal(HttpListenerContext context)
        {
            var request = new KannonRequest {
                HttpMethod = context.Request.HttpMethod,
                Url = context.Request.Url,
                InputStream = context.Request.InputStream
            };
            
            foreach(var header in context.Request.Headers.AllKeys)
                request.Headers[header] = context.Request.Headers[header];

            try
            {
                var application = (KannonApplication)Activator.CreateInstance(applicationType);
                var response = application.Handle(request) ?? new KannonResponse(HttpStatusCode.NotImplemented);

                WriteResponse(response, context);
            }
            catch(Exception e)
            {
                context.Response.StatusCode = 500;
                var message = Encoding.UTF8.GetBytes(e.ToString());
                context.Response.OutputStream.Write(message, 0, message.Length);
                context.Response.OutputStream.Close();
            }
        }

        private void WriteResponse(KannonResponse response, HttpListenerContext context)
        {
            context.Response.StatusCode = (int)response.StatusCode;

            if(!string.IsNullOrEmpty(response.StatusDescription))
                context.Response.StatusDescription = response.StatusDescription;

            SetResponseHeaders(response, context);

            if(response.OutputStream != null)
            {
                var buffer = new byte[1024];
                var bytesRead = response.OutputStream.Read(buffer, 0, 1024);
                do
                {
                    context.Response.OutputStream.Write(buffer, 0, bytesRead);
                    bytesRead = response.OutputStream.Read(buffer, 0, 1024);
                } 
                while(bytesRead > 0);

                response.OutputStream.Dispose();
            } // if

            context.Response.OutputStream.Close();
        }

        private void SetResponseHeaders(KannonResponse response, HttpListenerContext context)
        {
            foreach(var header in response.Headers)
            {
                var value = response.Headers[header.Value];

                if(RestrictedHeaderActions.ContainsKey(header.Key))
                    RestrictedHeaderActions[header.Key](context.Response, value);
                else
                    context.Response.Headers[header.Key] = value;
            } // foreach
        }
    }
}